package com.victualler.core.common.utils;

import com.victualler.core.common.enums.ChecKDataErrorEnum;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;


/**
 * @author Guoshichang
 * @since  15:56 on 2018/7/12.
 * @Function  压缩或解压zip：由于直接使用java.util.zip工具包下的类，会出现中文乱码问题，所以使用ant.jar中的org.apache.tools.zip下的工具类
 */
public class ZipUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(ZipUtil.class);



    /**
     * @author Guoshichang
     * @since 17:21 on 2018/7/12.
     * @param zipPath 压缩文件的目的地
     * @param sourcePath 被压缩的文件地址
     * @Function 将sourcePath文件夹下所有文件压缩为zipPath下的文件
     * @return  被压缩的文件
     */
    public static File zipFileByPath( String zipPath ,String sourcePath){
        List<File> srcFiles  = new ArrayList<>();
        srcFiles.add(new File(sourcePath));
        return zipFiles(zipPath,srcFiles);
    }

    /**
     * @author Guoshichang
     * @since 17:23 on 2018/7/12.
     * @param zipPath 压缩的目的地址
     * @param srcFiles 压缩的源文件列表  (可以是多个文件夹)
     * @Function  压缩文件或路径
     * @return 被压缩的文件
     */
    public static File zipFiles( String zipPath , List<File> srcFiles ){
        try {
            if( zipPath.endsWith(".zip") || zipPath.endsWith(".ZIP") ){
                LOGGER.info("---------------[开始压缩]------------------");
                File zipFile = new File(zipPath);
                if(!zipFile.getParentFile().exists()){
                    zipFile.getParentFile().mkdirs();
                }
                //创建压缩文件输出流
                ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(new File(zipPath))) ;
                zipOut.setEncoding("GBK");
                //将多个文件或文件夹扫描压缩到此文件中
                for( File file : srcFiles ){
                    handlerFile(zipPath , zipOut , file , "");
                }
                zipOut.flush();
                zipOut.close();
                LOGGER.info("---------------[压缩成功]------------------");
                return zipFile;
            }else{
                throw  ChecKDataErrorEnum.createException(ChecKDataErrorEnum.CHECK_ERROR,"target file[" + zipPath + "] is not .zip type file");
            }
        } catch (Exception e) {
            throw  ChecKDataErrorEnum.createException(ChecKDataErrorEnum.RUN_ERROR,e.getMessage());
        }
    }

    /**
     * @author Guoshichang
     * @since 17:23 on 2018/7/12.
     * @param zipPath 压缩的目的地址
     * @param zipOut
     * @param srcFile  被压缩的文件信息
     * @param path  在zip中的相对路径
     * @Function
     */
    private static void handlerFile(String zipPath , ZipOutputStream zipOut , File srcFile , String path ) throws IOException{
        LOGGER.info("压缩文件:[" + srcFile.getAbsolutePath() + "]");
        if( !"".equals(path) && !path.endsWith(File.separator)){
            path += File.separator ;
        }
        if(!srcFile.getPath().equals(zipPath)){
            if( srcFile.isDirectory() ){
                File[] files = srcFile.listFiles() ;
                if( files.length == 0 ){
                    //文件夹加入
                    zipOut.putNextEntry(new ZipEntry( path + srcFile.getName() + File.separator));
                    zipOut.closeEntry();
                }else{
                    //递归调用
                    for( File file : files ){
                        handlerFile(zipPath ,zipOut , file , path + srcFile.getName() );
                    }
                }
            }else{
                //文件
                InputStream in = new FileInputStream(srcFile) ;
                zipOut.putNextEntry(new ZipEntry(path + srcFile.getName()));
                byte[] bytes = new byte[1024] ;
                int len = 0 ;
                while( (len = in.read(bytes)) > 0  ){
                    zipOut.write(bytes, 0, len);
                }
                in.close();
                zipOut.closeEntry();
            }
        }
    }


    /**
     * @author Guoshichang
     * @since 16:34 on 2018/7/12.
     * @param zipPath 压缩文件的路径
     * @param realPath 解压缩的文件夹
     * @Function
     */
    public static List<File> upzipFileByPath(String zipPath, String realPath) {
        return upzipFile( new File(zipPath) , realPath ) ;
    }

    /**
     * @author Guoshichang
     * @since 17:24 on 2018/7/12.
     * @param zpFile  待解压缩的文件
     * @param realPath 解压缩的目标文件夹地址，如：D:\\测试 或 /mnt/d/测试
     * @Function  对.zip文件进行解压缩
     */
    @SuppressWarnings("rawtypes")
    public static List<File> upzipFile(File zpFile, String realPath) {
        LOGGER.info("--------------解压开始--------------");
        List<File> list = new ArrayList<>() ;
        try {
            ZipFile zipFile = new ZipFile(zpFile , "GBK") ;
            //遍历压缩目录 一个一个解压文件
            for(Enumeration entries = zipFile.getEntries() ; entries.hasMoreElements(); ){
                ZipEntry entry = (ZipEntry)entries.nextElement() ;
                File file = new File(realPath + File.separator + entry.getName()) ;
                LOGGER.info("解压成功:"+file.getAbsolutePath());
                if( entry.isDirectory() ){
                    file.mkdirs() ;
                }else{
                    File parent = file.getParentFile() ;
                    if(!parent.exists() ){
                        parent.mkdirs() ;
                    }
                    InputStream in = zipFile.getInputStream(entry);
                    OutputStream out = new FileOutputStream(file) ;
                    byte[] bytes = new byte[1024] ;
                    int len = 0 ;
                    while( (len = in.read(bytes)) > 0){
                        out.write(bytes, 0, len);
                    }
                    in.close();
                    out.flush();
                    out.close();
                    list.add(file) ;
                }
            }
            zipFile.close();
            LOGGER.info("--------------解压结束--------------");
            LOGGER.info("解压文件条目:"+list.size());
            return list ;
        } catch (Exception e) {
            e.printStackTrace();
            throw ChecKDataErrorEnum.createException(ChecKDataErrorEnum.RUN_ERROR,e.getMessage());
        }

    }

    /**
     * @author Guoshichang
     * @since 17:06 on 2018/7/12.
     * @param realPath 要保存的文件路径
     * @param content 文件内容
     * @Function 将指定内容保存到对应的realPath文件中
     */
    public static File saveStringToFile(String realPath,String content){
        File file = new File(realPath);
        //创建文件夹
        if(!file.getParentFile().exists() ){
            file.getParentFile().mkdirs() ;
        }
        try {
            FileOutputStream out = new FileOutputStream(file);
            out.write(content.getBytes("UTF-8"));
            out.flush();
            out.close();
            return file;
        } catch (Exception e) {
            e.printStackTrace();
            throw ChecKDataErrorEnum.createException(ChecKDataErrorEnum.RUN_ERROR,e.getMessage());
        }
    }



    /**
     * @author Guoshichang
     * @since 16:49 on 2018/7/12.
     * @param delpath
     * @Function  递归删除文件夹下的所有文件
     */
    public static void deleteAllfileByPath(String delpath) {
        try {
            File file = new File(delpath);
            if (!file.isDirectory()) {
                file.delete();
            } else if (file.isDirectory()) {
                String[] filelist = file.list();
                for (int i = 0; i < filelist.length; i++) {

                    File delfile = new File(delpath + File.separator + filelist[i]);
                    if (!delfile.isDirectory()) {
                        LOGGER.info("删除:"+delfile.getAbsolutePath());
                        delfile.delete();
                    } else if (delfile.isDirectory()) {
                        deleteAllfileByPath(delpath + File.separator + filelist[i]);
                    }
                }

                file.delete();
            }
            LOGGER.info("删除:"+file.getAbsolutePath());
        } catch (Exception e) {
            e.printStackTrace();
            throw ChecKDataErrorEnum.createException(ChecKDataErrorEnum.RUN_ERROR,e.getMessage());
        }
    }


    /**
     * @author Guoshichang
     * @since 20:24 on 2018/7/19.
     * @Function  在项目classpath生成一个临时的目录用于存放临时文件  文件夹为md5串 便于删除
     */
    public static String genTempBaseFileDirPath() {
        //自动生成一个唯一的文件夹 用于操作临时文件
        return ZipUtil.class.getClassLoader().getResource("").getPath()+"/temp/"+UUIDUtil.getUUID()+"/";
    }



}
